import * as ws from 'ws';
import { generateRandomString } from '../../utils/idGenerator';
import { SignalMessage } from '../type';

export class Room { 
    private id: string;
    private name: string;
    private clients: Map<string, ws.WebSocket>;
    private maxClients: number;
    private createAt: Date;

    constructor(id?:string,name= "default", maxClients=1000000) {
        this.id = id?id:generateRandomString();
        this.name = name;
        this.clients = new Map<string, ws.WebSocket>();
        this.maxClients = maxClients;
        this.createAt = new Date();
    }

    public getRoomId(): string {
        return this.id;
    }

    public getRoomName(): string {
        return this.name;
    }

    public getClientsCount(): number {
        return this.clients.size;
    }

    public removeClient(clientId: string): void { 
        this.clients.delete(clientId);
    }

    public isFull(): boolean { 
        return this.clients.size >= this.maxClients;
    }

    public isEmpty(): boolean { 
        return this.clients.size === 0;
    }

    public broadcast(message: any,excludeClientId?: string){
        this.clients.forEach((ws:ws.WebSocket,clientId:string)=>{
            if(excludeClientId && excludeClientId === clientId) return;
            ws.send(message);
        })
    }

    public handleOffer(data:SignalMessage){
        this.handlePeerToPeerMessage(data, "offer")
    }

    public handleAnswer(data:SignalMessage){
        this.handlePeerToPeerMessage(data, "answer")
    }

    public handleCandidate(data:SignalMessage){
        if (!("from" in data && "payload" in data && data.type === "candidate")) {
            console.log("Invalid candidate message",data);
            return;
        }
        const {from} = data;
        this.clients.forEach((ws:ws.WebSocket,clientId:string)=>{
            if (clientId !== from) {
                ws.send(JSON.stringify(data));
            }
        })
    }

    private handlePeerToPeerMessage(data: SignalMessage, messageType: string): void {
        if (!("from" in data && "to" in data && "payload" in data && data.type === messageType)) {
            console.log(`Invalid ${messageType} message`, data);
            return;
        }
        
        const { from, to } = data;
        const fromClient = this.clients.get(from);
        const toClient = this.clients.get(to);
        
        if (!fromClient || !toClient) {
            console.log("Client not found");
            return;
        }
        
        toClient.send(JSON.stringify(data));
    }

    public joinRoom(clientId: string, websocket: ws.WebSocket): boolean { 
        if (!this.addClient(clientId, websocket))return false;

        this.returnJoinedMsg(clientId);

        return true;
    }

    private addClient(clientId: string, websocket: ws.WebSocket): boolean { 
        if (this.clients.size >= this.maxClients){
            console.log("Room is full")
            return false;
        }
        this.clients.set(clientId,websocket)
        console.log(`Added client ${clientId} to room ${this.id}`);
        return true
    }

    private returnJoinedMsg(joinId:string){
        this.clients.get(joinId)!.send(JSON.stringify({
            type:"joined",
            roomId:this.id,
            clientId:joinId
        } as SignalMessage))

        this.clients.forEach((value,key)=>{
            if(key !== joinId){
                value.send(JSON.stringify({
                    type:"other_joined",
                    roomId:this.id,
                    joinerId:joinId,
                    to:key
                } as SignalMessage))
            }
        })
    }
}